#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
using namespace std;

void handler(int signo)
{
    cout << "我是父进程 " << getpid() << "我收到了子进程发送过来的信号" << signo << endl;
}

// 子进程在退出的时候，会给父进程发送SIGCHLD信号
// 当我们知道了这一点后，父进程就不再需要同步的等待子进程了
// 而是通过信号来通知父进程有子进程退出
void test_SIGCHLD()
{
    // 对SIGCHLD信号进行捕获
    signal(SIGCHLD, handler);
    // 创建子进程
    int pid = fork();
    // 分流
    if (pid == 0) // 子进程
    {
        cout << "我是子进程 " << getpid() << " 我的父进程是 " << getppid() << endl;
        sleep(1);
        cout << "我子进程退出了" << endl;
        exit(0);
    }
    // 父进程
    int n = waitpid(pid, nullptr, 0);
    if (n > 0)
        cout << "等待成功, n = " << n << endl;
}


void waitChild(int signo)
{
    // 我们写的下面一份代码对吗？
    // 如果有很多个子进程，我们能把所有子进程都等待到吗?
    // 并不可以，只等待成功2个 为什么呢？
    // 不要忘记信号捕获时，会屏蔽自身这个信号，只会发送一次
    // 即当一个子进程退出时，我在处理这个子进程的退出，
    // 此时又有多个子进程退出，又会发送SIG_CHLD，记录在信号屏蔽字中，但只有1个
    // 对某个退出的子进程进行捕获后，其他退出哪些子进程就再也捕获不到了。信号丢失了。

    // cout << "who " << getpid() << endl;
    // sleep(2);
    // int n = waitpid(-1, nullptr, 0);
    // if (n > 0)
    //     cout << "等待成功, n = " << n << endl;

    // 如何解决？
    // 我们需要用循环，在处理这个信号捕获期间，把所有退出的子进程都给处理了

    // 还有没有更好的方案？有！在Linux下，对SIG_CHLD进行SIG_IGN 捕获，默认会回收子进程！！

    cout << "捕获信号成功 signo = " << signo << endl;
    sleep(1);
    while(true)
    {
        // 不能使用阻塞等待。
        // 如果使用阻塞等待，只要还存在子进程没有退出，那么此时waitpid就会阻塞等待，等待所有子进程退出
        // 父进程就会被阻塞在信号捕获这里
        // 只能使用 WNOHANG进行等待
        // int n = waitpid(-1,nullptr,0);
        int n = waitpid(-1,nullptr,WNOHANG);

        if(n > 0)
        {
            cout << "等待成功，子进程pid = " << n << endl;
        }
        else if(n == 0) // 已经没有可等待的子进程成了，则这一次信号捕捉处理结束
        {
            break;
        }
        // -1的情况，在处理第一次捕获时，又有子进程退出，block表上SIG_CHLD被标记，但是在此次捕获中，所有子进程都已经等待成功了
        // 当此次捕获结束后，会再次捕获(block表中的)，那么此时已经没有进程等待了，那么就会一直等待失败导致无法出循环，也需要break
        // 简洁一点两个条件可以合并
        else 
        {
            break;
        }
    }

}

void test_new_wait()
{
    // 对SIGCHLD信号进行捕获
    // signal(SIGCHLD, waitChild);

    // Linux，对SIGCHLD信号，以SIG_IGN方式捕获，系统自动会回收子进程
    signal(SIGCHLD,SIG_IGN);
    // 创建子进程
    for(int i = 0; i < 10; i++)
    {
        int pid = fork();
        // 分流
        if (pid == 0) // 子进程
        {
            cout << "我是子进程 " << getpid() << " 我的父进程是 " << getppid() << endl;
            if(i < 5)
                sleep(1);
            else    
                sleep(6);
            cout << "我子进程退出了" << endl;
            exit(0);
        }
    }
    int cnt = 0;
    while(true)
    {
        cnt++;
        if(cnt == 15)
            break;
        cout << "我是父进程，我在做其他事情" << endl;
        sleep(1);
    }
}

int main()
{
    test_new_wait();
    return 0;
}